/*
 * Command.java
 *
 * Created on 29 octobre 2007, 14:03
 */
package com.reimscopie.console ;

import java.io.* ;
import java.util.* ;


/**
 * This interface must be used to implement a shell command.
 *
 * @see Console
 */
public abstract class Command {
  
  // variables
  private Map<String,Object> variables ;
  
  
  /**
   * Creates a new <code>Command</code> instance.
   */
  protected Command() {
    
  }
  
  
  /**
   * Return the name of this command. The name is the first word in the shell
   * command line.
   *
   * @return The command name.
   */
  public abstract String getName() ;
  
  
  /**
   * Return a short help message about this command.
   *
   * @return The command help.
   */
  public abstract String getHelp() ;
  
  
  /**
   * Return the command usage. This string must give all command options with
   * a description for each option.
   *
   * @return The command usage.
   */
  public abstract String getUsage() ;
  
  
  /**
   * Execute this command.
   *
   * @param args The arguments given in the command line.
   * @param output The command output.
   *
   * @exception CommandException The command failed.
   */
  public abstract void execute(String[] args,ConsoleOutput output)
  throws CommandException ;
  
  /**
   * Set all variables.
   *
   * @param variables The variable map.
   */
  void setVariableMap(Map<String,Object> variables) {
    this.variables = variables ;
  }
  
  
  /**
   * Set a variable.
   *
   * @param name The variable name.
   * @param value The variable value.
   */
  protected void setVariable(String name,Object value) {
    variables.put(name,value) ;
  }
  
  
  /**
   * Returns a variable value.
   *
   * @param name The variable.
   *
   * @return The variable value, or <code>null</code> is the name was not
   * found.
   */
  protected Object getVariable(String name) {
    return variables.get(name) ;
  }
  
  
  /**
   * Returns all referenced variable names.
   *
   * @return The variables names.
   */
  protected String[] getVariableNames() {
    
    java.util.Set<String> set = variables.keySet() ;
    
    String[] array = new String[set.size()] ;
    set.toArray(array) ;
    
    return array ;
  }
  
  
  /**
   * Return an option value.
   *
   * @param name The option name.
   * @param args All command arguments.
   *
   * @return The option value, or <code>null</code> if the option is not
   * specified.
   */
  protected String getOption(String name,String[] args) {
    
    if (args == null)
      return null ;
    
    String option = '-'+name ;
    
    for (int i=0;i<args.length-1;i++) {
      
      if (option.equals(args[i])) {
        return args[i+1] ;
      }
    }
    
    return null ;
  }
  
  
  /**
   * Return an option value. An exception is thrown if the option is not found.
   *
   * @param name The option name.
   * @param args All command arguments.
   *
   * @return The option value.
   *
   * @exception CommandException The option is not specified.
   */
  protected String requireOption(String name,String[] args)
  throws CommandException {
    
    String value = getOption(name,args) ;
    
    if (value == null)
      throw new CommandException("option required: "+name) ;
    else
      return value ;
    
  }
  
  
  /**
   * Return an option as an integer. An exception is thrown if the option is
   * not found, or is not an integer.
   *
   * @param name The option name.
   * @param args All command arguments.
   *
   * @return The option value.
   *
   * @exception CommandException The option is not specified or not an integer.
   */
  protected int requireIntegerOption(String name,String[] args)
  throws CommandException {
    
    String value = requireOption(name,args) ;
    
    try {
      return Integer.parseInt(value) ;
    }
    catch (NumberFormatException e) {
      throw new CommandException("integer expected: "+name,e) ;
    }
    
  }
  
  
  /**
   * Return an option as a float. An exception is thrown if the option is
   * not found, or is not a float.
   *
   * @param name The option name.
   * @param args All command arguments.
   *
   * @return The option value.
   *
   * @exception CommandException The option is not specified or not a float.
   */
  protected float requireFloatOption(String name,String[] args)
  throws CommandException {
    
    String value = requireOption(name,args) ;
    
    try {
      return Float.parseFloat(value) ;
    }
    catch (NumberFormatException e) {
      throw new CommandException("float expected: "+name,e) ;
    }
    
  }
  
  
  /**
   * Return an option as a float. An exception is thrown if the option is
   * not found, or is not a float.
   *
   * @param name The option name.
   * @param args All command arguments.
   *
   * @return The option value.
   *
   * @exception CommandException The option is not specified or not a float.
   */
  protected boolean requireBooleanOption(String name,String[] args)
  throws CommandException {
    
    String value = requireOption(name,args) ;
    
    // true
    if (value.equals("true") || value.equals("yes"))
      return true ;
    
    // false
    else if (value.equals("false") || value.equals("no"))
      return false ;
    
    // error
    else
      throw new CommandException("boolean expected: "+name) ;
    
  }
  
  
  /**
   * Return a parameter value. Options are skipped.
   *
   * @param index The parameter index in the command line.
   * @param args All command arguments.
   *
   * @return The parameter value, or <code>null</code> if the parameter
   * is not specified.
   */
  protected String getParameter(int index,String[] args) {
    
    if (args == null)
      return null ;
    
    for (int i=0;i<args.length;i++) {
      
      if (args[i].charAt(0) == '-')
        i++ ;
      else {
        if (index == 0)
          return args[i] ;
        index-- ;
      }  
      
    }
    
    return null ;
  }
  
  
  /**
   * Return a parameter value. Options are skipped. An exception is thrown if
   * the parameter is not specified.
   *
   * @param name The parameter name.
   * @param index The parameter index.
   * @param args All command arguments.
   *
   * @return The parameter value.
   *
   * @exception CommandException The parameter is not specified.
   */
  protected String requireParameter(String name,int index,String[] args)
  throws CommandException {
    
    String value = getParameter(index,args) ;
    
    if (value == null)
      throw new CommandException("parameter required: "+name) ;
    else
      return value ;
  }
  
  
  /**
   * Return a parameter value as an integer. Options are skipped. An exception
   * is thrown if the parameter is not specified.
   *
   * @param name The parameter name.
   * @param index The parameter index.
   * @param args All command arguments.
   *
   * @return The parameter value.
   *
   * @exception CommandException The parameter is not specified, or is not
   * an integer.
   */
  protected int requireIntegerParameter(String name,int index,String[] args)
  throws CommandException {
    
    String value = requireParameter(name,index,args) ;
    
    try {
      return Integer.parseInt(value) ;
    }
    catch (NumberFormatException e) {
      throw new CommandException("integer expected: "+name,e) ;
    }
    
  }
  
  
  /**
   * Return a parameter value as a float. Options are skipped. An exception
   * is thrown if the parameter is not specified.
   *
   * @param name The parameter name.
   * @param index The parameter index.
   * @param args All command arguments.
   *
   * @return The parameter value.
   *
   * @exception CommandException The parameter is not specified, or is not
   * a float.
   */
  protected float requireFloatParameter(String name,int index,String[] args)
  throws CommandException {
    
    String value = requireParameter(name,index,args) ;
    
    try {
      return Float.parseFloat(value) ;
    }
    catch (NumberFormatException e) {
      throw new CommandException("float expected: "+name,e) ;
    }
    
  }
  
}
